# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: leon

import functools

import pandas
from pandas.io.sql import to_sql
from tabulate import tabulate

from coin.exchange.okex.kr_rest.product import OkexProduct
from coin.support.feed_tool.feed_stats.logic.util import *
from coin.support.feed_tool.feed_stats.logic.instrument_stats_calculator import (
    BitmexInstrumentStatsCalculator,
    OkexInstrumentStatsCalculator,
    OkexV3InstrumentStatsCalculator,
    OkexIndexStatsCalculator,
    InstrumentStats)


class DumpStrategy(object):
  def __init__(self, machine, products, trading_date: datetime.datetime, subscription_request):
    self._machine = machine
    self._products = products
    self._trading_date = trading_date
    self._exchange = subscription_request.exchange
    self._subscription_request = subscription_request
    self._instrument_stats_calculators = {}

  def subscribe_and_create_calculator(self, book_builder, product):
    if self._exchange == 'Bitmex':
      book_builder.subscribe_instrument(product, functools.partial(self.on_instrument, product))
      calculator = BitmexInstrumentStatsCalculator(self._trading_date,
                                                   self._machine,
                                                   self._subscription_request)
    elif self._exchange == 'Okex':
      if self._subscription_request.api_version == 'futures':
        book_builder.subscribe_ticker(product, functools.partial(self.on_ticker, product))
        calculator = OkexInstrumentStatsCalculator(self._trading_date,
                                                   self._machine,
                                                   self._subscription_request)
        underlying_product = OkexProduct(product.base, product.quote)
        if underlying_product not in self._instrument_stats_calculators:
          book_builder.subscribe_index(underlying_product,
                                       functools.partial(self.on_index, underlying_product))
          index_calculator = OkexIndexStatsCalculator(self._trading_date,
                                                      self._machine,
                                                      self._subscription_request)
          index_calculator.set_product(underlying_product)
          self._instrument_stats_calculators[underlying_product] = \
              index_calculator
      elif self._subscription_request.api_version in ['v3', 'v3_swap']:
        book_builder.subscribe_ticker(product, functools.partial(self.on_ticker, product))
        book_builder.subscribe_instrument(product, functools.partial(self.on_instrument, product))
        calculator = OkexV3InstrumentStatsCalculator(self._trading_date,
                                                     self._machine,
                                                     self._subscription_request)
      else:
        raise ValueError(self._subscription_request)
    else:
      raise ValueError(self._exchange)
    calculator.set_product(product)
    self._instrument_stats_calculators[product] = calculator

  def on_feed_reset(self, book_builder_name, book_builder):
    assert book_builder_name == self._subscription_request
    for product in self._products:
      self.subscribe_and_create_calculator(book_builder, product)

  def on_ticker(self, product, ticker):
    instrument_stats_calculator = \
        self._instrument_stats_calculators[product]
    instrument_stats_calculator.update_by_instrument(ticker)

  def on_index(self, product, index):
    instrument_stats_calculator = \
        self._instrument_stats_calculators[product]
    instrument_stats_calculator.update_by_instrument(index)

  def on_instrument(self, product, instrument):
    instrument_stats_calculator = \
        self._instrument_stats_calculators[product]
    instrument_stats_calculator.update_by_instrument(instrument)

  def print_instrument_stats(self):
    stats_table = []
    for product, calculator in sorted(self._instrument_stats_calculators.items()):
      calculator.calculator_stats()
      stats = calculator.instrument_stats
      stats_table += stats
    if len(stats_table) == 0:
      print('No instrument feed stats for this run!')
    else:
      print(tabulate(stats_table, headers=InstrumentStats._fields))

  def dump_to_db(self, db_config_path):
    engine = gen_engine_for_importer()
    for product, calculator in sorted(self._instrument_stats_calculators.items()):
      calculator.calculator_stats()
      stats = calculator.instrument_stats

      df = pandas.DataFrame(stats, columns=InstrumentStats._fields)
      to_sql(df, 'InstrumentStats', engine, if_exists='append', index=False)

  def generate_csv(self, output_csv_root):
    for product, calculator in sorted(self._instrument_stats_calculators.items()):
      calculator.generate_csv(output_csv_root)
